#include "common.h"
#include "AAPolygonRenderer.h"

#ifdef DIRECTX
#define ALPHA 64
#endif
#ifdef OPT_OPENGL
#define ALPHA 255
#endif


#define ALPHA_COLOR(a) (((a) << 24) | ((a) << 16) | ((a) << 8) | (a))


using namespace Scenic::Renderer;


static void __stdcall tessBegin(GLenum mode)
{
	if(mode != GL_TRIANGLES)
	{
		printf("Tesselator begin: NOT TRIANGLES!!!\n");
	}
}

static void __stdcall tessEnd()
{
}

static void __stdcall tessEdgeFlag(GLboolean flag, AAPolygonRenderer *renderer)
{
	renderer->edgeFlag(flag ? true : false);
}

static void __stdcall tessCombineFunc(double coords[3], struct TessVertex *d[4], float w[4], struct TessVertex **dataOut,
							AAPolygonRenderer *renderer)
{
	renderer->combineFunc(coords, d, w, dataOut);
}

static void __stdcall tessVertexFunc(struct TessVertex *data, AAPolygonRenderer *renderer)
{
	renderer->vertexFunc(data);
}


AAPolygonRenderer::AAPolygonRenderer(Context *context)
{
	this->context = context;
	vs = new VertexStore(VSF_TEX1, context, 0);
	subVs = new VertexStore(VSF_TEX1, context, 0);
	addVs = new VertexStore(VSF_TEX1, context, 0);
	corner = 0;

	color0 = 0;
	color = ALPHA_COLOR(ALPHA);
	color2 = ALPHA_COLOR(ALPHA / 2);
	color8 = ALPHA_COLOR(ALPHA / 8);
	filterLength = 0.5;

	if(context->aaPolygonTexture == 0)
		createTexture();
}

AAPolygonRenderer::~AAPolygonRenderer()
{
	delete vs;
	delete subVs;
	delete addVs;
}

void AAPolygonRenderer::begin()
{
	context->setPrimitives(0);
	tessObj = gluNewTess();

	gluTessNormal(tessObj, 0, 0, 1);
	gluTessProperty(tessObj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);
	gluTessCallback(tessObj, GLU_TESS_BEGIN, (GLvoid (__stdcall *)())tessBegin);
	gluTessCallback(tessObj, GLU_TESS_VERTEX_DATA, (GLvoid (__stdcall *)())tessVertexFunc); 
	gluTessCallback(tessObj, GLU_TESS_END, (GLvoid (__stdcall *)())tessEnd);
	gluTessCallback(tessObj, GLU_TESS_COMBINE_DATA, (GLvoid (__stdcall *)())tessCombineFunc);
	gluTessCallback(tessObj, GLU_TESS_EDGE_FLAG_DATA, (GLvoid (__stdcall *)())tessEdgeFlag);
	gluTessBeginPolygon(tessObj, this);

	context->device->setTexture(0, context->aaPolygonTexture);
	context->device->setSamplerStates(0, FILTER_LINEAR, ADDRESS_CLAMP);
	context->device->setTextureStageModulation(0, 1);
	context->device->setBlendFunc(BLEND_ONE, BLEND_ONE);
}

void AAPolygonRenderer::end()
{
	corner = 0;

	gluTessEndPolygon(tessObj);	
	gluDeleteTess(tessObj);
	draw();
	context->device->setTexture(0, NULL);
}

void AAPolygonRenderer::beginContour()
{
	gluTessBeginContour(tessObj);
}

void AAPolygonRenderer::endContour()
{
	gluTessEndContour(tessObj);
}

void AAPolygonRenderer::addPoint(double x, double y)
{
	struct TessVertex *v = tessVertexBuffer.newVertex();
	Affine2 tm = context->transform;

	v->p[0] = tm.m00 * x + tm.m01 * y + tm.dx;
	v->p[1] = tm.m10 * x + tm.m11 * y + tm.dy;
	v->p[2] = 0;
	
	gluTessVertex(tessObj, v->p, v);	
}

void AAPolygonRenderer::draw()
{
	vs->draw();
	addVs->draw();
	context->device->setState(STATE_BLENDOP, BLENDOP_REVSUBTRACT);
	context->device->setState(STATE_BLENDOPALPHA, BLENDOP_REVSUBTRACT);
	subVs->draw();
	context->device->setState(STATE_BLENDOP, BLENDOP_ADD);
	context->device->setState(STATE_BLENDOPALPHA, BLENDOP_ADD);
}

void AAPolygonRenderer::edgeFlag(bool edge)
{
	edgeState = edge;
}

void AAPolygonRenderer::combineFunc(double coords[3], struct TessVertex *d[4], 
										   float w[4], struct TessVertex **dataOut)
{
	struct TessVertex *v = tessVertexBuffer.newVertex();
	
	v->p[0] = coords[0];
	v->p[1] = coords[1];
	v->p[2] = coords[2];
		
	*dataOut = v;
}


void AAPolygonRenderer::vertexFunc(struct TessVertex *data)
{
	triangle[corner] = data;
	triangleEdge[corner] = edgeState;
	corner++;
	if(corner == 3)
	{
		processTriangle();
		corner = 0;
	}
}

void AAPolygonRenderer::processTriangle()
{
	int i;

	for(i = 0; i < 3; i++)
	{
		TessVertex *data = triangle[i];

		vs->addVertex(data->p[0], data->p[1], color, 1.0, 1.0);
	}
	vs->addTriangle(0, 1, 2);
	vs->endBlock();

	for(i = 0; i < 3; i++)
	{
		if(triangleEdge[i])
		{
			TessVertex *v1 = triangle[i];
			TessVertex *v2 = triangle[(i + 1) % 3];
	
			drawEdge2(v1, v2);
		}
	}
}

void AAPolygonRenderer::drawEdge(TessVertex *v1, TessVertex *v2)
{
	Double2 d;
	Double2 n;
	double m;
	double w = 1;

	n.x = v2->p[0] - v1->p[0];
	n.y = v2->p[1] - v1->p[1];

	m = w / sqrt(n.x * n.x + n.y * n.y);

	d.x = -n.y * m;
	d.y = n.x * m;

	vs->addVertex(v1->p[0] - d.x, v1->p[1] - d.y, color0);
	vs->addVertex(v2->p[0] - d.x, v2->p[1] - d.y, color0);
	vs->addVertex(v1->p[0], v1->p[1], color);
	vs->addVertex(v2->p[0], v2->p[1], color);

	vs->addTriangle(0, 1, 2);
	vs->addTriangle(2, 1, 3);
	vs->endBlock();
}

void AAPolygonRenderer::addCorner(Double2 p, double sign, double color)
{
/*
	int addAlpha = (ALPHA / 8) + (int)((ALPHA / 8) * color);
	int addColor = ALPHA_COLOR(addAlpha);
	int subAlpha = (ALPHA / 8) - (int)((ALPHA / 8) * color);
	int subColor = ALPHA_COLOR(subAlpha);

	vs->addVertex(p.x, p.y, addColor);
	vs->addVertex(p.x - filterLength, p.y, color0);
	vs->addVertex(p.x, p.y - sign * filterLength, color0);
	vs->addVertex(p.x + filterLength, p.y, color0);
	vs->addVertex(p.x, p.y + sign * filterLength, color0);

	vs->addTriangle(0, 1, 2);
	vs->addTriangle(0, 3, 4);
	vs->endBlock();

	subVs->addVertex(p.x, p.y, subColor);
	subVs->addVertex(p.x - filterLength, p.y, color0);
	subVs->addVertex(p.x, p.y + sign * filterLength, color0);
	subVs->addVertex(p.x + filterLength, p.y, color0);
	subVs->addVertex(p.x, p.y - sign * filterLength, color0);

	subVs->addTriangle(0, 1, 2);
	subVs->addTriangle(0, 3, 4);
	subVs->endBlock();
*/
//	color = 0.0;
	int addAlpha = (ALPHA / 8) + (int)((ALPHA / 8) * color);
	int addColor = ALPHA_COLOR(addAlpha);
	int subAlpha = (ALPHA / 8) - (int)((ALPHA / 8) * color);
	int subColor = ALPHA_COLOR(subAlpha);

	addVs->addVertex(p.x, p.y, addColor, 0.75, 0.75);
	addVs->addVertex(p.x - filterLength, p.y, addColor, 0.75, 0.25);
	addVs->addVertex(p.x, p.y - sign * filterLength, addColor, 0.25, 0.75);
	addVs->addVertex(p.x - filterLength, p.y - sign * filterLength, addColor, 0.25, 0.25);
	addVs->addVertex(p.x + filterLength, p.y, addColor, 0.75, 0.25);
	addVs->addVertex(p.x, p.y + sign * filterLength, addColor, 0.25, 0.75);
	addVs->addVertex(p.x + filterLength, p.y + sign * filterLength, addColor, 0.25, 0.25);

	addVs->addTriangle(0, 1, 2);
	addVs->addTriangle(1, 3, 2);
	addVs->addTriangle(0, 4, 5);
	addVs->addTriangle(4, 6, 5);
	addVs->endBlock();

	subVs->addVertex(p.x, p.y, subColor, 0.75, 0.75);
	subVs->addVertex(p.x - filterLength, p.y, subColor, 0.75, 0.25);
	subVs->addVertex(p.x, p.y + sign * filterLength, subColor, 0.25, 0.75);
	subVs->addVertex(p.x - filterLength, p.y + sign * filterLength, subColor, 0.25, 0.25);
	subVs->addVertex(p.x + filterLength, p.y, subColor, 0.75, 0.25);
	subVs->addVertex(p.x, p.y - sign * filterLength, subColor, 0.25, 0.75);
	subVs->addVertex(p.x + filterLength, p.y - sign * filterLength, subColor, 0.25, 0.25);

	subVs->addTriangle(0, 1, 2);
	subVs->addTriangle(1, 3, 2);
	subVs->addTriangle(0, 4, 5);
	subVs->addTriangle(4, 6, 5);
	subVs->endBlock();
}

void AAPolygonRenderer::drawEdge2(TessVertex *v1, TessVertex *v2)
{
	Double2 d;
	Double2 n;

	n.x = v2->p[0] - v1->p[0];
	n.y = v2->p[1] - v1->p[1];

	d.x = 0;
	d.y = 0;

	if(fabs(n.x) > fabs(n.y))
		d.y = n.x > 0.0 ? filterLength : -filterLength;
	else
		d.x = n.y < 0.0 ? filterLength : -filterLength;

	addVs->addVertex(v1->p[0] - d.x, v1->p[1] - d.y, color0, 1.0, 1.0);
	addVs->addVertex(v2->p[0] - d.x, v2->p[1] - d.y, color0, 1.0, 1.0);
	addVs->addVertex(v1->p[0], v1->p[1], color2, 1.0, 1.0);
	addVs->addVertex(v2->p[0], v2->p[1], color2, 1.0, 1.0);

	addVs->addTriangle(0, 1, 2);
	addVs->addTriangle(2, 1, 3);
	addVs->endBlock();

	subVs->addVertex(v1->p[0] + d.x, v1->p[1] + d.y, color0, 1.0, 1.0);
	subVs->addVertex(v2->p[0] + d.x, v2->p[1] + d.y, color0, 1.0, 1.0);
	subVs->addVertex(v1->p[0], v1->p[1], color2, 1.0, 1.0);
	subVs->addVertex(v2->p[0], v2->p[1], color2, 1.0, 1.0);

	subVs->addTriangle(0, 1, 2);
	subVs->addTriangle(2, 1, 3);
	subVs->endBlock();

	Double2 p1 = {v1->p[0], v1->p[1]};
	Double2 p2 = {v2->p[0], v2->p[1]};

	if(d.y == 0.0) 
	{
		if(n.y == 0.0)
			return;
		addCorner(p1, -1.0, n.x / n.y);
		addCorner(p2, 1.0, -n.x / n.y);
	}
	else 
	{
		if(n.x == 0.0)
			return;
		addCorner(p1, 1.0, -n.y / n.x);
		addCorner(p2, -1.0, n.y / n.x);
	}
}

void AAPolygonRenderer::createTexture()
{
	context->aaPolygonTexture = new Texture(2, 2, FMT_A8R8G8B8, 0);
	int dest[4];

	dest[0] = 0x00000000;
	dest[1] = 0x00000000;
	dest[2 + 0] = 0x00000000;
	dest[2 + 1] = 0xffffffff;

	context->aaPolygonTexture->write(0, 0, 0, 2, 2, dest, 2);
}
